/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 * Description: support rtos trace umount error feature
 * Author: lijiahuan
 * Create: 2022-05-08
 */
#include <linux/rtos_sysctl.h>
#include "rtos_trace_umount_error.h"

#define rtos_mount_trace(arg, ...) pr_info("RTOS MOUNT TRACE: "arg, ##__VA_ARGS__)
#define rtos_umount_trace(arg, ...) pr_info("RTOS UMOUNT TRACE: "arg, ##__VA_ARGS__)

static int trace_umount_error_enabled;

void rtos_umount_fail_trace(int ret, char __user *name, int flags)
{
	char *rtos_umount_trace_name = NULL;

	/* umount success or feature not enabled, no record */
	if (ret == 0 || trace_umount_error_enabled == 0)
		return;

	rtos_umount_trace_name = copy_mount_string(name);
	if (IS_ERR(rtos_umount_trace_name)) {
		rtos_umount_trace("umount not success ret %d, cannot get umount name\n", ret);
	} else {
		rtos_umount_trace("umount not success ret %d, name: %s, flag: 0x%x\n",
				ret, rtos_umount_trace_name, flags);
		kfree(rtos_umount_trace_name);
	}
}

int rtos_mount_domount_fail_trace(int ret, const char *kernel_type, const char *kernel_dev, unsigned long flags,
		char __user *dir_name, void __user *data)
{
	char *rtos_mount_trace_dir = NULL;
	void *rtos_mount_trace_options = NULL;

	/* mount success or feature not enabled, no record */
	if (ret == 0 || trace_umount_error_enabled == 0)
		return 0;

	rtos_mount_trace_dir = copy_mount_string(dir_name);
	if (IS_ERR(rtos_mount_trace_dir))
		goto rtos_mount_trace_end;

	/*
	 * mount syscall options variable is changed during do_mount function, so reget it.
	 */
	rtos_mount_trace_options = copy_mount_options(data);
	if (IS_ERR(rtos_mount_trace_options)) {
		kfree(rtos_mount_trace_dir);
		goto rtos_mount_trace_end;
	}
	rtos_mount_trace("mount dev %s not success ret %d, dir_name: %s, "
			"type: %s, flags: 0x%lx, options: %s\n",
			kernel_dev, ret, rtos_mount_trace_dir, kernel_type,
			flags, (char *)rtos_mount_trace_options);
	kfree(rtos_mount_trace_options);
	kfree(rtos_mount_trace_dir);
	return 1;
rtos_mount_trace_end:
	rtos_mount_trace("mount dev %s not success ret %d, "
		"cannot get params in trace process\n", kernel_dev, ret);
	return 1;
}

void rtos_mount_getparam_fail_trace(int ret, int mark)
{
	/* mount success or feature not enabled or fail after do_mount, no record */
	if (ret == 0 || trace_umount_error_enabled == 0 || mark == 1)
		return;

	rtos_mount_trace("mount not success ret %d, "
		"cannot get params before do_mount\n", ret);
}

static struct ctl_table rtos_trace_umount_error_sysctls[] = {
	{
		.procname       = "trace_umount_error",
		.data           = &trace_umount_error_enabled,
		.maxlen         = sizeof(int),
		.mode           = 0640,
		.proc_handler   = proc_dointvec_minmax,
		.extra1         = SYSCTL_ZERO,
		.extra2         = SYSCTL_ONE,
	},
	{}
};

rtos_kernel_sysctls_proc_init(rtos_trace_umount_error, rtos_trace_umount_error_sysctls);
